/* * Sun Public License Notice * * The contents of this file are subject to the Sun Public License * Version 1.0 (the "License"). You may not use this file except in * compliance with the License. A copy of the License is available at * http://www.sun.com/ * * The Original Code is NetBeans. The Initial Developer of the Original * Code is Sun Microsystems, Inc. Portions Copyright 1997-2000 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.modules.properties; import java.io.*; import java.lang.ref.SoftReference; import java.util.LinkedList; import java.util.HashMap; import javax.swing.text.BadLocationException; import org.openide.filesystems.FileObject; import org.openide.loaders.MultiDataObject; import org.openide.util.*; import org.openide.nodes.Children; import org.openide.text.PositionRef; import org.openide.text.PositionBounds; /* Handling of properties structure files * * @author Petr Hamernik, Petr Jiricka */ public class StructHandler extends Element /*implements TaskListener*/ { public static final String PROP_PARSE = "parse"; /** Appropriate properties file entry. */ private PropertiesFileEntry pfe; /** If the parsing is in progress this variable is set * to the parsing task. */ Task parsingTask; /** Soft reference to the data */ SoftReference dataRef; /** SoftReference is GC-ed too often, I wonder if I should keep a hard reference */ DataRef hardReference; /** This flag is set when somebody is editing the document and it is * cleared after reparsing. * It is used by parser to decide if parsing is necessary. */ boolean dirty = false; // ======================== Public part ==================================== static final long serialVersionUID =-3367087822606643886L; /** Constructs the implementation of source element for the given * java data object. */ public StructHandler(PropertiesFileEntry pfe) { super(null); this.pfe = pfe; } /** Getter for the current status of the SourceElement implementation. * @return the status. */ public boolean getStatus() { return getReferenceData() != null; } /** runs something under Children.MUTEX.writeAccess and then reparses the structure. */ /* public Object doUpdate(Mutex.Action action) { }*/ /** If necessary parses the file, blocks until the thing is finished */ private synchronized void getParsedDataBlocking() { if (isDirty() || (getReferenceData() == null)) { reparseNowBlocking(); } } synchronized void reparseNowBlocking() { try { PropertiesParser parser = new PropertiesParser(pfe); parser.parseFile(); } catch (IOException e) { setPropertiesStructure(null); } setDirty(false); } /** Entry for use in this package */ PropertiesFileEntry getEntry() { return pfe; } /** Method that instructs the implementation of the source element * to prepare the element. It is non blocking method that returns * task that can be used to control if the operation finished or not. * * @return task to control the preparation of the elemement */ /* public Task prepare () { return (Task) Children.MUTEX.writeAccess(new Mutex.Action() { public Object run() { if (parsingTask == null) { DataRef d = getReferenceData(); if (d != null) { return new DataTask(d); } parsingTask = createParsingTask(Thread.MAX_PRIORITY); } return parsingTask; } }); }*/ /** Get a string representation of the element for printing. * @return the string */ public String printString() { try { return getData().ps.printString(); } catch (PropertiesException e) { // PENDING - handle it return ""; } } // ======================== Package private part ================================ /** Sets the dirty flag - if the document was modified after last parsing. */ void setDirty(boolean b) { if (dirty == b) return; synchronized (this) { // another check inside the synchronized block if (dirty == b) return; dirty = b; } } /** Tests the dirty flag. This indicates whether the document and the structure match. */ boolean isDirty() { return dirty; } /** Starts the parsing if the this class is 'dirty' and status is true * and parsing is not running yet. */ void autoParse() { if (dirty && getStatus()) getParsedDataBlocking(); /* Children.MUTEX.writeAccess(new Runnable() { public void run() { if (dirty && (parsingTask == null) && getStatus()) { prepareParsing(Thread.MIN_PRIORITY); } } });*/ } /** This method invokes the parsing only. It could be used by * editor during saving. */ /* Task prepareParsing(final int priority) { return (Task) Children.MUTEX.writeAccess(new Mutex.Action() { public Object run() { if (parsingTask == null) { parsingTask = createParsingTask(priority); } return parsingTask; } }); }*/ /** When parser finishes its job, it has to call this method to inform * everyone about the result. Must be called under mutex.writeaccess * * @param res resultant structure */ synchronized void setPropertiesStructure(final PropertiesStructure res) { if (res == null) return; PropertiesStructure result = res; // effectively getReferenceData, but we're under writeAccess, so no readAccess DataRef data = (dataRef != null) ? (DataRef) dataRef.get() : null; if (data == null) { // set the parent res.setParent(this); data = new DataRef(pfe, res); dataRef = new SoftReference(data); hardReference = data; data.ps.structureChanged(); } else { // update calls notification methods according to changes data.ps.update(res); } setDirty(false); } /** Create parsing task. * May be called only under mutex.writeAccess() */ /* private Task createParsingTask(final int priority) { Runnable parseRunnable = new Runnable() { public void run() { try { PropertiesParser parser = new PropertiesParser(pfe); parser.parseFile(); } catch (IOException e) { setPropertiesStructure(null); } } }; RequestProcessor.Task t = RequestProcessor.postRequest(parseRunnable, 0, priority); t.addTaskListener(this); return t; } */ /** Gets the referenced object from the dataRef */ private DataRef getReferenceData() { return (DataRef) Children.MUTEX.readAccess(new Mutex.Action() { public Object run() { return (dataRef != null) ? (DataRef) dataRef.get() : null; } }); } /** Clear the parsing task variable */ /* public void taskFinished(final Task task) { Children.MUTEX.writeAccess(new Runnable() { public void run() { parsingTask = null; } }); }*/ /** Returns the structure */ public PropertiesStructure getStructure() { try { return getData().ps; } catch (PropertiesException e) { // PENDING return null; } } /** * * @return the DataRef object holding the parsing information * @exception SourceException if parsing failed. */ private DataRef getData() throws PropertiesException { DataRef d = getReferenceData(); if (d != null) return d; /* Task t = prepare(); t.waitFinished();*/ getParsedDataBlocking(); d = getReferenceData(); if (d != null) return d; throw new PropertiesException("Document cannot be modified. Impossible to parse it."); } /** Informs the SourceElement about releasing data (classes, imports,...) * from the memory. This method gets as the parameter DataRef which will be * garbage collected and should swap them to the disk. */ private void dataRefReleased(DataRef data) { Object oldValue = Children.MUTEX.writeAccess(new Mutex.Action() { public Object run() { dataRef = null; return new Boolean(true); } }); // PENDING //firePropertyChange (PROP_STATUS, null, null); } // ======================== The real data holder ========================== /** Class which is used for holding the parsed information. * It is serializable and could be swapped to the disk. * The struct handler holds only soft reference to this object. */ private static class DataRef extends Object { /** A serial version UID */ //static final long serialVersionUID = 697350931687937673L; /** Appropriate file entry. */ PropertiesFileEntry pfe; // --------------- Data ------------------- /** The structure holding the data */ PropertiesStructure ps; /** Creates new data holder. */ DataRef(PropertiesFileEntry pfe, PropertiesStructure ps) { this.pfe = pfe; this.ps = ps; } /** Informs the SourceElementImpl about the releasing * of this class from the memory. */ public void finalize() throws Throwable { if (pfe != null) { pfe.getHandler().dataRefReleased(this); } super.finalize(); } } // ======================== Utility - DataTask ========================== /** Task which is used for holding the reference to the given data. * It prevents them from being garbage collected. */ /* private static class DataTask extends Task { private DataRef data; public DataTask(DataRef data) { super(null); this.data = data; } }*/ } /* * <<Log>> * 15 Gandalf 1.14 11/27/99 Patrik Knakal * 14 Gandalf 1.13 10/23/99 Ian Formanek NO SEMANTIC CHANGE - Sun * Microsystems Copyright in File Comment * 13 Gandalf 1.12 9/23/99 Petr Jiricka Fixed calling * super.finalize() - throws Throwable (JLint) * 12 Gandalf 1.11 9/23/99 Petr Jiricka Finalize calls * super.finalize() (JLint warning) * 11 Gandalf 1.10 9/10/99 Petr Jiricka * 10 Gandalf 1.9 8/18/99 Petr Jiricka Debug prints * 9 Gandalf 1.8 8/9/99 Petr Jiricka Removed debug prints * 8 Gandalf 1.7 7/24/99 Petr Jiricka * 7 Gandalf 1.6 6/9/99 Ian Formanek ---- Package Change To * org.openide ---- * 6 Gandalf 1.5 6/8/99 Petr Jiricka * 5 Gandalf 1.4 6/6/99 Petr Jiricka * 4 Gandalf 1.3 5/16/99 Petr Jiricka * 3 Gandalf 1.2 5/14/99 Petr Jiricka * 2 Gandalf 1.1 5/13/99 Petr Jiricka * 1 Gandalf 1.0 5/12/99 Petr Jiricka * $ */